home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / SCSI Samples 1.0 / SCSI VBL Sample 06⁄07 ƒ / Src / SCSIVBLSample.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-16  |  25.5 KB  |  746 lines  |  [TEXT/KAHL]

  1. /*                                SCSIVBLSample.c                            */
  2. /*
  3.  * SCSIVBLSample.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  * This is a minimal sample to illustrate calling the SCSI Manager from a
  6.  * VBL task. It displays drive ready/not ready status changes.
  7.  *
  8.  * This program requires SCSI Manager 4.3.
  9.  */
  10. #include <Errors.h>
  11. #include <Script.h>
  12. #include <Types.h>
  13. #include <Files.h>
  14. #include <Resources.h>
  15. #include <QuickDraw.h>
  16. #include <Fonts.h>
  17. #include <Events.h>
  18. #include <Windows.h>
  19. #include <ToolUtils.h>
  20. #include <Memory.h>
  21. #include <Menus.h>
  22. #include <Lists.h>
  23. #include <Printing.h>
  24. #include <Dialogs.h>
  25. #include <StandardFile.h>
  26. #include <Packages.h>
  27. #include <Retrace.h>
  28.  
  29. #include "SCSIVBLSample.h"
  30.  
  31. #ifndef FALSE
  32. #define FALSE    0
  33. #define TRUE    1
  34. #endif
  35.  
  36. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  37.  * As of this writing, the Asynchronous SCSI Manager definitions have not
  38.  * been added to the standard header distribution. This include references
  39.  * a working version that is stored in the application folder hierarchy.
  40.  *
  41.  * Include the O.S. files in a specific order to make sure that we have
  42.  * a definition for the _SCSIAtomic trap.
  43.  */
  44. #include <Traps.h>
  45. #ifndef _SCSIAtomic
  46. #define _SCSIAtomic    0xA089
  47. #endif
  48. #include "SCSI.h"
  49.  
  50. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  51.  * Define the device that we will use. Note that the orignnal SCSI Manager
  52.  * only uses kSCSITargetID, while the asynchronous SCSI Manager also uses
  53.  * the bus and LUN data.  You must set this for your particular system.
  54.  * configuration. (Remember, this is a test.)
  55.  */
  56. #ifndef kSCSIBusID
  57. #define kSCSIBusID        1        /* Quadra 950 external bus                */
  58. #endif
  59. #ifndef kSCSITargetID
  60. #define kSCSITargetID    4        /* Specific to my P8100 (a CD device)    */
  61. //#define kSCSITargetID    2        /* Specific to my Q950 (a CD device)    */
  62. #endif
  63. #ifndef kSCSILUN
  64. #define kSCSILUN        0        /* A pretty safe choice                    */
  65. #endif
  66.  
  67. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  68.  * SCSI Definitions
  69.  * We will issuing a Device Inquiry to the device. In a real application,
  70.  * we would probably just issue a Test Unit Ready, but this code was done
  71.  * to try to duplicate a developer problem, so we're doing the operation
  72.  * that they had a problem with. Upon debugging, I discovered that the
  73.  * particular device I'm working with did not return Check Condition
  74.  * if Device Inquiry was issued and no CD was inserted. Thus, we switch
  75.  * between two commands.
  76.  */
  77. #define kScsiCmdTestUnitReady    0x00        /* Test unit ready command    */
  78. #define    kScsiCmdInquiry            0x12        /* Device inquiry command    */
  79.  
  80. struct SCSI_6_Byte_Command {                /* Six-byte command            */
  81.     unsigned char        opcode;                /*  0                        */
  82.     unsigned char        lbn3;                /*  1 lbn in low 5            */
  83.     unsigned char        lbn2;                /*  2                        */
  84.     unsigned char        lbn1;                /*  3                        */
  85.     unsigned char        len;                /*  4                        */
  86.     unsigned char        ctrl;                /*  5                        */
  87. };
  88. typedef struct SCSI_6_Byte_Command SCSI_6_Byte_Command;
  89.  
  90. struct SCSI_Inquiry_Data {                    /* Inquiry returns this        */
  91.     unsigned char        devType;            /*  0 Device type,            */
  92.     unsigned char        devTypeMod;            /*  1 Device type modifier    */
  93.     unsigned char        version;            /*  2 ISO/ECMA/ANSI version    */
  94.     unsigned char        format;                /*  3 Response data format    */
  95.     unsigned char        length;                /*  4 Additional Length        */
  96.     unsigned char        reserved5;            /*  5 Reserved                */
  97.     unsigned char        reserved6;            /*  6 Reserved                */
  98.     unsigned char        flags;                /*  7 Capability flags        */
  99.     unsigned char        vendor[8];            /*  8-15 Vendor-specific    */
  100.     unsigned char        product[16];        /* 16-31 Product id            */
  101.     unsigned char        revision[4];        /* 32-35 Product revision    */
  102.     unsigned char        vendorSpecific[20]; /* 36-55 Vendor stuff        */
  103.     unsigned char        moreReserved[40];    /* 56-95 Reserved            */
  104. };
  105. typedef struct SCSI_Inquiry_Data SCSI_Inquiry_Data;
  106.  
  107. struct SCSI_Sense_Data {                /* Request Sense result            */
  108.     unsigned char        errorCode;        /*  0    Class code, valid lbn    */
  109.     unsigned char        segmentNumber;    /*  1    Segment number            */
  110.     unsigned char        senseKey;        /*  2    Sense key and flags        */
  111.     unsigned char        info[4];
  112.     unsigned char        additionalSenseLength;
  113.     unsigned char        reservedForCopy[4];
  114.     unsigned char        additionalSenseCode;
  115.     unsigned char        additionalSenseQualifier;    
  116.     unsigned char        fruCode;        /* Field replacable unit code    */
  117.     unsigned char        senseKeySpecific[2];
  118.     unsigned char        additional[101];
  119. };
  120. typedef struct SCSI_Sense_Data SCSI_Sense_Data;
  121. /*
  122.  * The high-bit of errorCode signals whether there is a logical
  123.  * block. The low value signals whether there is a valid sense
  124.  */
  125. #define kScsiSenseHasLBN            0x80    /* Logical block number set    */
  126. #define kScsiSenseInfoValid            0x70    /* Is sense key valid?        */
  127. #define kScsiSenseInfoMask            0x70    /* Mask for sense info        */
  128. /*
  129.  * These bits may be set in the sense key
  130.  */
  131. #define kScsiSenseKeyMask            0x0F
  132. #define kScsiSenseILI                0x20    /* Illegal logical Length    */
  133. #define kScsiSenseEOM                0x40    /* End of media                */
  134. #define kScsiSenseFileMark            0x80    /* End of file mark            */
  135.  
  136. /*
  137.  * SCSI sense codes. (Returned after request sense).
  138.  */
  139. #define     kScsiSenseNone                0x00    /* No error                    */
  140. #define     kScsiSenseRecoveredErr        0x01    /* Warning                    */
  141. #define     kScsiSenseNotReady            0x02    /* Device not ready            */
  142. #define     kScsiSenseMediumErr        0x03    /* Device medium error        */
  143. #define     kScsiSenseHardwareErr        0x04    /* Device hardware error    */
  144. #define     kScsiSenseIllegalReq        0x05    /* Illegal request for dev.    */
  145. #define     kScsiSenseUnitAtn            0x06    /* Unit attention (not err)    */
  146. #define     kScsiSenseDataProtect        0x07    /* Data protection            */
  147. #define     kScsiSenseBlankCheck        0x08    /* Tape-specific error        */
  148. #define     kScsiSenseVendorSpecific    0x09    /* Vendor-specific error    */
  149. #define     kScsiSenseCopyAborted        0x0a    /* Copy request cancelled    */
  150. #define     kScsiSenseAbortedCmd        0x0b    /* Initiator aborted cmd.    */
  151. #define     kScsiSenseEqual            0x0c    /* Comparison equal            */
  152. #define     kScsiSenseVolumeOverflow    0x0d    /* Write past end mark        */
  153. #define     kScsiSenseMiscompare        0x0e    /* Comparison failed        */
  154. #define     kScsiSenseCurrentErr        0x70
  155. #define     kScsiSenseDeferredErr        0x71
  156.  
  157. /*
  158.  * We switch between these two commands:
  159.  */
  160. #define kIssueDeviceInquiry            0        /* Must be zero                */
  161. #define kIssueTestUnitReady            1        /* Must be one                */
  162.  
  163. /*
  164.  * This will select the correct message to display in the alert.
  165.  */
  166. typedef enum {                        /* Device rediness state            */
  167.     kDeviceInitialState = 0,        /* Initializing, must be zero        */
  168.     kDeviceNotPresent,                /* No device at this bus id            */
  169.     kDeviceNotReady,                /* TestUnitReady/CheckCondition        */
  170.     kDeviceReady                    /* TestUnitReady/statusGood            */
  171. } DeviceState;
  172.  
  173. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  174.  * This is the VBL task structure that we need. All data is allocated on
  175.  * the System Heap to avoid problems with virtual memory. (But, note that
  176.  * the VBL task is in the application heap, so you should never run this
  177.  * test with virtual memory enabled).
  178.  *
  179.  * This extended  VBL structure has all of the data that the VBL and
  180.  * SCSI command needs.
  181.  *
  182.  * Operational state is defined by the following variables. They are
  183.  * actually application-specific globals, but are defined as elements of
  184.  * the ExtendedVBLTask record to eliminate the need to reference A5 from
  185.  * the VBL and SCSI completion routine. This simplifies extension of this
  186.  * code to support multiple devices. It would be better to combine these
  187.  * variables into a coherent finite-state automaton.
  188.  *
  189.  *    deviceState                Set by the SCSI Manager to one of the three
  190.  *                            "active" states: not present, not ready, or
  191.  *                            ready. If this differs from oldDeviceState,
  192.  *                            the application event loop will post an alert.
  193.  *        Set by:        SCSI Completion,
  194.  *        Read by:    Application event loop
  195.  *    oldDeviceState            The state that the application knows about.
  196.  *                            Note: in the real world, we might use a
  197.  *                            message buffer held in an OS Queue to avoid
  198.  *                            any possible race conditions.
  199.  *        Set by:        Application Event loop
  200.  *        Read by:    Application Event loop
  201.  *    quitNow                    Set by the application when the user hits
  202.  *                            a key or clicks the "Exit" button. Read by
  203.  *                            the VBL. If set, the VBL does not post an I/O
  204.  *                            request and does not post a VBL timer request.
  205.  *        Set by:        Application Event Loop
  206.  *        Read by:    VBL Task
  207.  *    vblActive                Set by the application when it installs the VBL
  208.  *                            at startup. Cleared by the VBL when it decides
  209.  *                            not to re-install itself, either because of a
  210.  *                            serious error (SCSI Manager error) or because
  211.  *                            the user quit and VBL.quitNow is TRUE. The
  212.  *                            application continues processing events while
  213.  *                            vblActive is TRUE; it exits when vblActive and
  214.  *                            scsiActive are both FALSE.
  215.  *        Set by:        Application startup
  216.  *        Cleared by:    VBL on exit
  217.  *        Read by        Application Event Loop
  218.  *    scsiActive                Set by the VBL when it starts an asynchronous
  219.  *                            I/O request, cleared by the completion routine.
  220.  *        Set by:        VBL on issuing SCSI I/O request
  221.  *        Cleared by:    SCSI Completion routine
  222.  *        Read by:    VBL to determine whether to issue another request,
  223.  *        Read by:    Application Event Loop on exit.
  224.  *
  225.  * Overall operational state (ignoring errors):
  226.  *    0.    All flags set FALSE.
  227.  *    1.    Create the VBL and SCSI parameter block, install the VBL, set
  228.  *        gVBLActive, and enter the event loop. Note that there is a
  229.  *        potential race condition if the first VBL fails. Work around
  230.  *        this by installing the VBL with a longer time delay.
  231.  *    2.    (Mainline): process events until any of the following occur:
  232.  *            deviceState != oldDeviceState:
  233.  *                        Copy deviceState to oldDeviceState and post an
  234.  *                        Alert with the new device state. Set quitNow if
  235.  *                        the device is non-existant.
  236.  *            vblActive and scsiActive are both FALSE: dispose of the VBL
  237.  *                        and SCSI parameter blocks. ExitToShell.
  238.  */
  239. typedef struct {
  240.     VBLTask                vblTask;            /* VBL Manager data            */
  241.     long                applicationA5;        /* To access strings etc.    */
  242.     unsigned long        vblCount;            /* VBL interrupts            */
  243.     unsigned long        scsiRequestCount;    /* SCSI requests issued        */
  244.     unsigned long        scsiCompleteCount;    /* SCSI requests completed    */
  245.     unsigned long        scsiWasBusy;        /* SCSI was busy in VBL        */
  246.     unsigned long        eventLoopCount;        /* WaitNextEvent cycles        */
  247.     unsigned long        scsiExecIOPBSize;    /* SCSI pb length            */
  248.     SCSIExecIOPB        *scsiPBPtr;            /* For new SCSI only        */
  249.     SCSI_Inquiry_Data    inquiryData;        /* Returned from the device    */
  250.     SCSI_Sense_Data        senseData;            /* Autosense information    */
  251.     short                commandSwitch;        /* Test Ready or Inquiry?    */
  252.     Boolean                quitNow;            /* TRUE to stop the VBL        */
  253.     Boolean                vblActive;            /* TRUE when VBL is running    */
  254.     Boolean                scsiActive;            /* TRUE during async I/O    */
  255.     DeviceState            deviceState;        /* Drive Inquiry result        */
  256.     DeviceState            oldDeviceState;        /* Event Manager's value    */
  257.     OSErr                scsiError;            /* SCSI Manager failure        */
  258. } ExtendedVBLTask, *ExtendedVBLTaskPtr;
  259. #define VBL        (*extendedVBLTaskPtr)
  260. #define SCSI    (*VBL.scsiPBPtr)
  261.  
  262. ExtendedVBLTaskPtr        gExtendedVBLTaskPtr;    /* The VBL "globals"    */
  263. #define kCompletionTimeout    (250)                /* 250 Msec total time    */
  264.  
  265. StringPtr                gDeviceStateString[] = {
  266.     "\pVBL initial state",
  267.     "\pDevice not present - cannot continue",
  268.     "\pDevice not ready, waiting for device ready",
  269.     "\pDevice ready"
  270. };
  271.  
  272. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  273.  * Local functions and administrative variables.
  274.  */
  275.  
  276. #ifdef __powerc
  277. QDGlobals                qd;
  278. #endif
  279.  
  280. void                        DisplayFinishedMessage(
  281.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  282.     );
  283. Boolean                        DisplayError(
  284.         OSErr                    errorStatus,
  285.         ConstStr255Param        errorMessage
  286.     );
  287. void                        DisplaySCSIInquiry(void);
  288. extern Boolean                AsyncSCSIPresent(void);
  289.  
  290. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  291.  * VBL and SCSI functions.
  292.  */
  293. ExtendedVBLTaskPtr            CreateVBLTask(void);
  294. void                        AllocateSCSIParamBlock(
  295.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  296.     );
  297. void                        InitializeSCSIParamBlock(
  298.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  299.     );
  300. void                        InstallVBLTask(
  301.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  302.     );
  303. pascal void                    MySCSICallbackProc(
  304.         SCSIExecIOPB            *scsiExecIOPBPtr
  305.     );
  306.  
  307. #ifdef __powerc
  308. pascal void                    MyVBLTask(
  309.         VBLTaskPtr                vblTaskPtr
  310.     );
  311. #else
  312. pascal void                    MyVBLTask(void);
  313. unsigned long        GetA0(void) = { 0x2008 };
  314. #endif
  315.  
  316. /*
  317.  * Cheap 'n dirty memory clear routine.
  318.  */
  319. #define CLEAR(record) do {                                \
  320.         register char    *ptr = (char *) &record;        \
  321.         register long    size;                            \
  322.         for (size = sizeof record; size > 0; --size)    \
  323.             *ptr++ = 0;                                    \
  324.     } while (0)
  325.  
  326. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  327.  * Event-loop mainline.
  328.  */
  329. void
  330. main(void)
  331. {
  332.         EventRecord                    eventRecord;
  333.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr;
  334.         
  335.         MaxApplZone();        
  336.         InitGraf(&qd.thePort);
  337.         InitFonts();
  338.         InitWindows();
  339.         InitMenus();
  340.         TEInit();
  341.         InitDialogs(0);
  342.         if (AsyncSCSIPresent() == FALSE) {
  343.             InitCursor();
  344.             StopAlert(ALRT_NoNewSCSI, NULL);
  345.             ExitToShell();
  346.         }
  347.         SetCursor(*GetCursor(watchCursor));
  348.         gExtendedVBLTaskPtr = extendedVBLTaskPtr = CreateVBLTask();
  349.         /*
  350.          * These functions ExitToShell on failure.
  351.          */
  352.         AllocateSCSIParamBlock(extendedVBLTaskPtr);
  353.         InstallVBLTask(extendedVBLTaskPtr);
  354.         /*
  355.          * Run the event loop
  356.          */
  357.         while (VBL.vblActive || VBL.scsiActive) {
  358.             WaitNextEvent(everyEvent, &eventRecord, 6, NULL);
  359.             ++VBL.eventLoopCount;
  360.             if (VBL.deviceState != VBL.oldDeviceState) {
  361.                 VBL.oldDeviceState = VBL.deviceState;
  362.                 ParamText(
  363.                     gDeviceStateString[VBL.oldDeviceState],
  364.                     "\p", "\p", "\p"
  365.                 );
  366.                 InitCursor();
  367.                 if (NoteAlert(ALRT_Info, NULL) == cancel
  368.                  || VBL.oldDeviceState == kDeviceNotPresent)
  369.                     VBL.quitNow = TRUE;
  370.                 SetCursor(*GetCursor(watchCursor));
  371.             }
  372.             if (VBL.scsiError != noErr) {
  373.                 VBL.quitNow |= DisplayError(
  374.                             VBL.scsiError,
  375.                             "\pAsynchronous SCSI error"
  376.                         );
  377.                 if (VBL.quitNow == FALSE)
  378.                     VBL.scsiError = noErr; 
  379.                 SetCursor(*GetCursor(watchCursor));
  380.             }
  381.             /*
  382.              * MouseDown doesn't work as there is no window to
  383.              * mouse into.
  384.              */
  385.             if (eventRecord.what == keyDown)
  386.                 VBL.quitNow = TRUE;
  387.         }
  388.         DisplayFinishedMessage(extendedVBLTaskPtr);
  389.         DisposePtr((Ptr) extendedVBLTaskPtr->scsiPBPtr);
  390.         DisposePtr((Ptr) extendedVBLTaskPtr);
  391.         ExitToShell();
  392. }
  393.  
  394. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  395.  * Display a message when we're done.
  396.  */
  397. void
  398. DisplayFinishedMessage(
  399.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  400.     )
  401. {
  402.         Str15                    vblCountMsg;
  403.         Str15                    scsiCompleteCountMsg;
  404.         Str15                    scsiWasBusyMsg;
  405.         Str15                    eventLoopCountMsg;
  406.         
  407.         NumToString(VBL.vblCount, vblCountMsg);
  408.         NumToString(VBL.scsiCompleteCount,    scsiCompleteCountMsg);
  409.         NumToString(VBL.scsiWasBusy,        scsiWasBusyMsg);
  410.         NumToString(VBL.eventLoopCount,        eventLoopCountMsg);
  411.         ParamText(
  412.             vblCountMsg,
  413.             scsiCompleteCountMsg,
  414.             scsiWasBusyMsg,
  415.             eventLoopCountMsg
  416.         );
  417.         InitCursor();
  418.         NoteAlert(ALRT_Finished, NULL);
  419. }
  420.  
  421. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  422.  * Display an error alert. Return TRUE if the user clicks "Exit"
  423.  */
  424. Boolean
  425. DisplayError(
  426.         OSErr                    errorStatus,
  427.         ConstStr255Param        errorMessage
  428.     )
  429. {
  430.         Str255                    work;
  431.         
  432.         if (errorStatus == noErr)
  433.             ParamText("\p(no error)", errorMessage, "\p", "\p");
  434.         else {
  435.             NumToString(errorStatus, work);
  436.             ParamText(work, errorMessage, "\p", "\p");
  437.         }
  438.         InitCursor();
  439.         return (StopAlert(ALRT_Error, NULL) == cancel);
  440. }
  441.  
  442. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  443.  * The following section contains all VBL and SCSI-specific code.
  444.  * Build the VBL "global" area. Called once during application startup.
  445.  * Note: under MetroWerks and Think, be sure to add saving and restoring
  446.  * register A4 if you incorporate this into a driver or other
  447.  * non-application code segment.
  448.  */
  449. ExtendedVBLTaskPtr
  450. CreateVBLTask(void)
  451. {
  452.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr;
  453.         
  454.         extendedVBLTaskPtr = (ExtendedVBLTaskPtr)
  455.                     NewPtrSysClear(sizeof (ExtendedVBLTask));
  456.         if (extendedVBLTaskPtr == NULL) {
  457.             DisplayError(MemError(), "\pCan't create extended VBL task");
  458.             ExitToShell();
  459.         }
  460.         VBL.applicationA5 = SetCurrentA5();    /* Link to appl. globals    */
  461.         VBL.vblTask.qType = vType;            /* VBL queue type            */
  462.         VBL.vblTask.vblAddr = NewVBLProc(MyVBLTask);
  463.         VBL.vblTask.vblCount = 60;            /* Initial long sleep        */
  464.         return (extendedVBLTaskPtr);
  465. }
  466.  
  467. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  468.  * Call the SCSIBusInquiry command to determine the length of the command
  469.  * block for the actual transfer. Called once during application startup.
  470.  */
  471. void
  472. AllocateSCSIParamBlock(
  473.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  474.     )
  475. {
  476.         SCSIBusInquiryPB        scsiBusInquiryPB;
  477.         OSErr                    status;
  478.         
  479.         CLEAR(scsiBusInquiryPB);
  480.         scsiBusInquiryPB.scsiPBLength = sizeof scsiBusInquiryPB;
  481.         scsiBusInquiryPB.scsiFunctionCode = SCSIBusInquiry;
  482.         scsiBusInquiryPB.scsiDevice.bus = kSCSIBusID;
  483.         scsiBusInquiryPB.scsiDevice.targetID = kSCSITargetID;
  484.         scsiBusInquiryPB.scsiDevice.LUN = kSCSILUN;
  485.         SCSIAction((SCSI_PB *) &scsiBusInquiryPB);
  486.         status = scsiBusInquiryPB.scsiResult;
  487.         if (status != noErr) {
  488.             DisplayError(status, "\pSCSIBusInquiry failed");
  489.             DisposePtr((Ptr) extendedVBLTaskPtr);
  490.             ExitToShell();
  491.         }
  492.         VBL.scsiExecIOPBSize = scsiBusInquiryPB.scsiIOpbSize;
  493.         VBL.scsiPBPtr = (SCSIExecIOPB *)
  494.             NewPtrSysClear(VBL.scsiExecIOPBSize);
  495.         if (VBL.scsiPBPtr == NULL) {
  496.             DisplayError(MemError(), "\pNo memory for param. block");
  497.             DisposePtr((Ptr) extendedVBLTaskPtr);
  498.             ExitToShell();
  499.         }
  500. }
  501.  
  502.  
  503.  
  504. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  505.  * Start the VBL task. If this returns, the application must exit by setting
  506.  * VBL.quitNow and waiting for both "active" flags to be set false.
  507.  */
  508. void
  509. InstallVBLTask(
  510.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  511.     )
  512. {
  513.         OSErr                    status;
  514.         
  515.         VBL.vblActive = TRUE;
  516.         status = VInstall((QElemPtr) extendedVBLTaskPtr);
  517.         if (status != noErr) {
  518.             DisplayError(MemError(), "\pCannot install VBL task");
  519.             DisposePtr((Ptr) VBL.scsiPBPtr);
  520.             DisposePtr((Ptr) extendedVBLTaskPtr);
  521.             ExitToShell();
  522.         }
  523. }
  524.  
  525. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  526.  * This is the VBL task that is entered on each vbl clock tick. (The first
  527.  * call is delayed to allow the application to reach a stable state).
  528.  * On entrance, it performs the following tasks:
  529.  *    1. If VBL.quitNow is set, it removes the VBL task, sets VBL.vblActive
  530.  *        FALSE, and exits.
  531.  *    2. if VBL.quitNow is FALSE, VBL.scsiActive is FALSE, and no error
  532.  *        status is pending, it posts the next SCSI Device Inquiry request
  533.  *        and continues at step 3.
  534.  *    3. Normal continuation: set vblCount to restart the VBL and exit. Note
  535.  *        that this might not have started a new I/O request.
  536.  */
  537. #ifdef __powerc
  538. pascal void
  539. MyVBLTask(
  540.         VBLTaskPtr                vblTaskPtr
  541.     );
  542. #else
  543. pascal void
  544. MyVBLTask(void)
  545. {
  546.         VBLTaskPtr                vblTaskPtr = (VBLTaskPtr) GetA0();
  547. #endif
  548.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr;
  549.         long                    oldA5;
  550.         OSErr                    status;
  551.         
  552.         extendedVBLTaskPtr = (ExtendedVBLTaskPtr) vblTaskPtr;
  553.         oldA5 = SetA5(VBL.applicationA5);
  554.         ++VBL.vblCount;
  555.         if (VBL.quitNow) {
  556.             /*
  557.              * Shutdown needed.
  558.              */
  559.             VBL.vblActive = FALSE;
  560.             status = VRemove((QElemPtr) vblTaskPtr);
  561.             if (status != noErr)
  562.                 DebugStr("\pVRemove error");    /* Can't happen            */
  563.         }
  564.         else {
  565.             VBL.vblTask.vblCount = 1;            /* Quickly return here    */
  566.             if (VBL.scsiActive)
  567.                 ++VBL.scsiWasBusy;
  568.             if (VBL.scsiActive == FALSE            /* SCSI op completed?    */
  569.               && VBL.scsiError == noErr) {        /* Error state clear?    */
  570.                 /*
  571.                  * No SCSI request is currently in progress and there is
  572.                  * no pending error condition. Post the next request.
  573.                  */
  574.                 VBL.scsiActive = TRUE;
  575.                 ++VBL.scsiRequestCount;
  576.                 InitializeSCSIParamBlock(extendedVBLTaskPtr);
  577.                 status = SCSIAction((SCSI_PB *) VBL.scsiPBPtr);
  578.                 /*
  579.                  * If we failed here, something is seriously wrong and the
  580.                  * SCSI parameter block was not accepted. In this case, the
  581.                  * completion routine will never be called, so we must
  582.                  * signal an error here. Note that we started the VBL
  583.                  * timer: only setting VBL.quitNow stops the VBL task.
  584.                  */
  585.                 if (status != noErr) {
  586.                     VBL.scsiActive = FALSE;
  587.                     ++VBL.scsiCompleteCount;
  588.                      if (VBL.scsiError == noErr)
  589.                         VBL.scsiError = status;
  590.                 }
  591.             }
  592.         }
  593.         (void) SetA5(oldA5);
  594. }
  595.  
  596. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  597.  * Setup the parameter block for the user's request. We do this each
  598.  * time through the loop to make sure that the parameter block is
  599.  * always consistent.
  600.  */
  601. void
  602. InitializeSCSIParamBlock(
  603.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr
  604.     )
  605. {
  606.         CLEAR(SCSI);
  607.         SCSI.scsiPBLength = VBL.scsiExecIOPBSize;
  608.         SCSI.scsiCompletion = (CallbackProc) MySCSICallbackProc;
  609.         SCSI.scsiFunctionCode = SCSIExecIO;
  610.         SCSI.scsiTimeout = kCompletionTimeout;
  611.         SCSI.scsiDevice.bus = kSCSIBusID;
  612.         SCSI.scsiDevice.targetID = kSCSITargetID;
  613.         SCSI.scsiDevice.LUN = kSCSILUN;
  614.         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  615.          * Specify the transfer direction, if any, and set the other SCSI
  616.          * operation flags. scsiSIMQNoFreeze prevents the SCSI Manager from
  617.          * blocking further operation if an error is detected.
  618.          */
  619.         SCSI.scsiFlags =
  620.                     scsiSIMQNoFreeze            /* Don't stop on errors    */
  621.                     | scsiDontDisconnect;        /* No disconnect        */
  622.         switch (VBL.commandSwitch) {
  623.         case kIssueDeviceInquiry:
  624.             SCSI.scsiCDB.cdbBytes[0] = kScsiCmdInquiry;
  625.             SCSI.scsiCDB.cdbBytes[4] = sizeof (SCSI_Inquiry_Data);
  626.             SCSI.scsiFlags |= scsiDirectionIn;
  627.             SCSI.scsiTransferType = scsiTransferPolled;
  628.             SCSI.scsiDataPtr = (unsigned char *) &VBL.inquiryData;
  629.             SCSI.scsiDataLength = sizeof VBL.inquiryData;
  630.             SCSI.scsiDataType = scsiDataBuffer;
  631.             break;
  632.         case kIssueTestUnitReady:
  633.             SCSI.scsiCDB.cdbBytes[0] = kScsiCmdTestUnitReady;
  634.             SCSI.scsiFlags |= scsiDirectionNone;
  635.             break;
  636.         }
  637.         SCSI.scsiCDBLength = sizeof (SCSI_6_Byte_Command);
  638.         SCSI.scsiSensePtr = (unsigned char *) &VBL.senseData;
  639.         SCSI.scsiSenseLength = sizeof VBL.senseData;
  640.         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  641.          * Turn off select with attention so the device doesn't disconnect.
  642.          * This is inefficient for most SCSI operations, but is reasonable
  643.          * the Device Identity, Mode Sense, Test Unit Ready, and some other
  644.          * other administrative commands.
  645.          */
  646.         SCSI.scsiIOFlags |= scsiDisableSelectWAtn;
  647.         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  648.          * Establish a linkage between the SCSI parameter block and the VBL.
  649.          * This lets us access the VBL structure from the SCSI completion
  650.          * procedure. Note that we could have done this using A5 and
  651.          * global information, but this method translates to what drivers
  652.          * must do in a clean manner.
  653.          */
  654.         SCSI.scsiDriverStorage = (unsigned char *) extendedVBLTaskPtr;
  655. }
  656.  
  657. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  658.  * This completion routine is called by the SCSI Manager when the
  659.  * SCSIAction procedure completes.
  660.  */
  661. pascal void
  662. MySCSICallbackProc(
  663.         SCSIExecIOPB            *scsiExecIOPBPtr
  664.     )
  665. {
  666.         register ExtendedVBLTaskPtr    extendedVBLTaskPtr;
  667.         long                        oldA5;
  668.         register SCSI_Sense_Data    *senseDataPtr;
  669. #define SENSE    (*senseDataPtr)
  670.         
  671.         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  672.          * Recover the extended VBL task pointer and, through it, the
  673.          * application A5 value.
  674.          */
  675.         extendedVBLTaskPtr =
  676.                 (ExtendedVBLTaskPtr) scsiExecIOPBPtr->scsiDriverStorage;
  677.         oldA5 = SetA5(VBL.applicationA5);
  678.         VBL.scsiActive = FALSE;
  679.         ++VBL.scsiCompleteCount;
  680.         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  681.          * Check for errors and set the device state accordingly:
  682.          *    noErr                -> kDeviceReady if Test Unit Ready
  683.          *    scsiDeviceNotThere    -> kDeviceNotPresent
  684.          *    scsiBusy            -> Do nothing: VBL will retry us.
  685.          *    scsiNonZeroStatus    -> (check autosense result)
  686.          *    other errors        -> Set VBL.scsiError.
  687.          */
  688.         if (SCSI.scsiResult == scsiDataRunError
  689.          && SCSI.scsiDataResidual >= 0)
  690.              SCSI.scsiResult = 0;
  691.         switch (SCSI.scsiResult) {
  692.         case noErr:
  693.             if (VBL.commandSwitch == kIssueTestUnitReady)
  694.                 VBL.deviceState = kDeviceReady;
  695.             VBL.commandSwitch = 1 - VBL.commandSwitch;    /* Normal        */
  696.             break;
  697.         case scsiSelectTimeout:
  698.         case scsiDeviceNotThere:
  699.             VBL.deviceState = kDeviceNotPresent;
  700.             break;
  701.         case scsiBusy:                    /* SCSI Subsystem busy            */
  702.             break;                        /* Re-issue this command        */
  703.         case scsiNonZeroStatus:
  704.             /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  705.              * Look at the error code in the autosense buffer.
  706.              */
  707.             senseDataPtr =
  708.                 (SCSI_Sense_Data *) scsiExecIOPBPtr->scsiSensePtr;
  709.             if ((SENSE.errorCode & kScsiSenseInfoMask)
  710.                         != kScsiSenseInfoValid) {
  711.                 if (VBL.scsiError == noErr)
  712.                     VBL.scsiError = scsiNonZeroStatus;    /* Bad sense    */
  713.             }
  714.             else {
  715.                 switch (SENSE.senseKey & kScsiSenseKeyMask) {
  716.                 case kScsiSenseNotReady:            /* Expected            */
  717.                     /*
  718.                      * In a real application, you would also notice
  719.                      * whether the "operator intervention needed" code
  720.                      * was present in the additionalSenseCode byte.
  721.                      * This indicates "no device inserted" (usually).
  722.                      * We should have done a TestUnitReady. In any case,
  723.                      * continue here until the sense key changes.
  724.                      */
  725.                     VBL.deviceState = kDeviceNotReady;
  726.                     break;
  727.                 case kScsiSenseNone:                /* No error            */
  728.                     VBL.commandSwitch = 1 - VBL.commandSwitch;
  729.                     break;
  730.                 case kScsiSenseRecoveredErr:        /* Retry warning    */
  731.                 case kScsiSenseUnitAtn:                /* Unit reset        */
  732.                     break;                            /* Just retry these    */
  733.                 default:                            /* Something is        */
  734.                     goto seriousError;                /*  very incorrect    */
  735.                 }                                    /* Which sense key    */
  736.             }                                        /* If ok sense key    */
  737.             break;                                    /* If sense error    */
  738.         default:
  739. seriousError:
  740.             if (VBL.scsiError == noErr)
  741.                 VBL.scsiError = SCSI.scsiResult;    /* Real trouble        */
  742.             break;
  743.         }
  744.         (void) SetA5(oldA5);
  745. }
  746.